/* Copyright 2024 Coremail
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Entity mapped to table "Mail".
 */
type EOF = -1;
import { Columns, ColumnType, Entity, Id } from '@ohos/dataorm'
import { MailAttribute } from '@coremail/mail_base';
import type {
  EmailAddress,
  MailBasic,
  MailEnvelope,
  MailStructure,
} from '@coremail/mail_base';

import { FileUtil } from '../utils/FileUtil';
import { getMimeParts } from '@coremail/mail_base/src/main/ets/utils/mime';

// export class AttachmentModel {
//   partId:string;
//   filePath:string;
// }

export type Attachment = {
  name: string;
  type: string;
  subType: string;
  id: string;
  encoding: string;
  size: number;
  index: number;
  localPath: string|null;
  partId: string;
}

export type Content  = {
  filePath:string;
  partId:string;
}

export enum MailContentType {
  HTML,
  TEXT
}


// export enum MailFlag {
//   NEW = 1 << 0, //未读
//   DELETE = 1 << 1, //删除
//   FLAG = 1 << 2, // 旗帜
//   STAR = 1 << 3, //星标
//   HAS_ATTACHMENT = 1 << 4,//有附件.
//   HAS_INLINEFILE = 1 << 5, //有内联文件.
// }


//表名
@Entity("mail", [{ value: "mid",  unique: false }])
export class MailEntity {

  static tableName = "mail";

  //自增id sqlite不支持创建自增非主键列.
  @Id({isPrimaryKey:true,autoincrement:true})
  @Columns({ columnName: "id", types: ColumnType.num })
  id: number;
  //邮件id
  @Columns({ columnName: "mid", types: ColumnType.str })
  mid: string
  //eml源文件大小.
  @Columns({ columnName: "size", types: ColumnType.num })
  size: number
  // 收信时间
  @Columns({ columnName: "received_date", types: ColumnType.num })
  receivedDate: number
  // from to, cc, bcc 存储Json 格式字符串
  @Columns({ columnName: "sender", types: ColumnType.str })
  sender: string



  @Columns({ columnName: "receiver", types: ColumnType.str })
  receiver: string

  @Columns({ columnName: "cc", types: ColumnType.str })
  cc: string

  @Columns({ columnName: "bcc", types: ColumnType.str })
  bcc: string

  //主题
  @Columns({ columnName: "subject", types: ColumnType.str })
  public subject:string;

  /**
   * 是否是Flag标志.
   * 和服务器同步的标志.
   */
  @Columns({ columnName: "is_flag", types: ColumnType.num })
  public isFlag:boolean;


  /**
   * 是否是新邮件.
   */
  @Columns({ columnName: "is_new", types: ColumnType.num })
  public isNew:boolean;

  /**
   * emlPath
   */
  @Columns({ columnName: "eml_path", types: ColumnType.str })
  public emlPath: string;


  /**
   * 附件文件 [{},{},{}...]
   * 附件信息集合（包含 fileName ， localPath等）
   * 外部附件.
   */
  @Columns({ columnName: "attachments", types: ColumnType.str })
  private attachments: string;
  private _attachments:Attachment[];
  public setAttachments(attchs:Attachment[]) : void {
    this._attachments = attchs;
    this.attachments = JSON.stringify(attchs);
    this.attachmentNames = attchs.map((item:Attachment)=>{
      return item.name
    }).join("");
  }

  public getAttachments(): Attachment[] {
    if(!this._attachments) {
      this._attachments = JSON.parse(this.attachments) as Attachment[];
      return this._attachments;
    }
  }

  /**
   * envelope json字符串.
   * _envelop 对应的MailEnvelop数据.
   *  通过setEnvelop和getEnvelop进行读写.
   */
  @Columns({ columnName: "envelope", types: ColumnType.str })
  private envelope:string;
  //解析出来的envelop
  private _envelop:MailEnvelope

  /**
   * 邮件结构
   * mailStructure json字符串.
   */
  @Columns({ columnName: "structure", types: ColumnType.str })
  public structure: string;
  private _structure:MailStructure


  @Columns({ columnName: "parsed", types: ColumnType.num })
  public parsed: boolean = false;

  /**
   * 外部附件文件名数组,用于搜索.
   * xxx1.jpgxxx2.jpgxxx3.jpg
   */
  @Columns({ columnName: "attachment_names", types: ColumnType.str })
  private attachmentNames: string;

  // //附件文件 [{},{},{}...] 内联图片数据集合（包含 contentId ， localPath 等）
  @Columns({ columnName: "inline_files", types: ColumnType.str })
  private inlineFiles: string;
  private _inlineFiles:Attachment[]
  public setInlineFiles(inlines:Attachment[]) : void {
    this._inlineFiles = inlines;
    this.inlineFiles = JSON.stringify(inlines);
  }

  public getInlineFiles(): Attachment[] {
    if(!this._inlineFiles) {
      this._inlineFiles = JSON.parse(this.inlineFiles) as Attachment[];
      return this._inlineFiles;
    }
  }

  /**
   * 是否具有保存到本地的文件.
   * 同时也是是否解析过的标志.
   *
   */
  hasPartContent():boolean {
    return this.html.length > 0 || this.text.length > 0 || this.attachments.length > 0 || this.inlineFiles.length > 0
  }

  //所属文件夹的full_name
  // folderId
  @Columns({ columnName: "folder_id", types: ColumnType.num })
  public folderId: number;

  // @Columns({ columnName: "folder_full_name", types: ColumnType.str })
  // public folderFullName: string;



  // to b 需要该字段，目前不确定to c是否也需要
  @Columns({ columnName: "is_local", types: ColumnType.num })
  public isLocal: boolean;

  // 扩展字段搜索使用
  @Columns({ columnName: "search_context", types: ColumnType.str })
  public searchContext: string;

  //保存正文的附件路径
  @Columns({ columnName: "text", types: ColumnType.str })
  private text: string;
  private _text?: Content;
  public setText(filePath:string,partId:string): void {
    let text = {
      filePath: filePath,
      partId: partId
    }
    this.text = JSON.stringify(text);
    this._text = text;
  }
  public getText(): Content | undefined {
    if(!this._text) {
      if(this.text && this.text.length > 0) {
        this._text = JSON.parse(this.text) as Content;
      }
    }
    return this._text
  }

  //保存html的附件路径
  @Columns({ columnName: "html", types: ColumnType.str })
  private html: string;
  private _html?: Content;

  public setHtml(filePath: string, partId: string): void {
    let html = {
      filePath: filePath,
      partId: partId
    }
    this.html = JSON.stringify(html)
    this._html = html;
  }
  public getHtml(): Content | undefined {
    if(!this._html) {
      if(this.html && this.html.length > 0) {
        this._html = JSON.parse(this.html) as Content;
      }
    }
    return this._html
  }


  getPartFilePath(partId: string): string|null {
    if (this.getText()?.partId === partId) {
      return this.getText().filePath;
    }
    else if (this.getHtml()?.partId === partId) {
      return this.getHtml().filePath;
    }
    else {
      let target = this.getAttachments().find((item) => {
        return item.partId === partId;
      })
      if (target) {
        return target.localPath;
      }
      target = this.getInlineFiles().find((item) => {
        return item.partId === partId;
      })
      if (target) {
        return target.localPath;
      }
      else {
        return null;
      }
    }
  }


  public getHtmlContent():string {
    let html = "";
    let content = this.getHtml();
    if(content.filePath) {
      html = FileUtil.getContent(content.filePath);
    }
    return html;
  }
  public getTextContent():string {
    let text = "";
    let content = this.getText();
    if(content.filePath) {
      text = FileUtil.getContent(content.filePath);
    }
    return text;
  }


  @Columns({ columnName: "summary", types: ColumnType.str })
  public digest:string;







  /*
   1,类中必须在constructor中声明所有非静态变量，用于反射生成列
   2,数据库生成实例,没有调用构造函数,如何初始化某些特定数据?
  */
  constructor(
    id?:number,
    mid?: string,
    size?:number,
    receivedDate?:number,
    sender?: string,
    structure?:string,
    envelope?:string,
    receiver?: string,
    cc?: string,
    bcc?: string,
    subject?:string,
    isFlag?:number,
    isNew?:number,
    emlPath?:string,
    attachments?:string,
    inlineFiles?:string,
    folderId?:number,
    // folderFullName?:string,
    isLocal?: boolean,
    searchContext? :string,
    attachmentNames?:string,
    text?:string,
    html?:string,
    digest?:string,
    parsed?:boolean
  ) {
    this.id = id
    this.mid = mid
    this.size = size
    this.receivedDate = receivedDate
    this.sender = sender
    this.receiver = receiver

    this.cc = cc
    this.bcc = bcc
    this.subject = subject;
    this.isFlag = !!isFlag;
    this.envelope = envelope;
    this.structure = structure;

    this.isNew = !!isNew;
    this.emlPath = emlPath;
    this.attachments = attachments;
    this.attachmentNames = attachmentNames;
    this.inlineFiles = inlineFiles;
    this.folderId = folderId;

    this.isLocal = isLocal;
    this.searchContext = searchContext;
    this.text = text;
    this.html = html;
    this.digest = digest;
    this.parsed = !!parsed
  }

  /**
   * 更新标志位
   *
   * ''
   * [] ==> 设为没有标记
   * [Seen] ==> 设为Seen标记
   * [Flagged] ==> 设为Flagged标记
   * [Seen,Flagged] ==> 添加两个标记.
   *
   * '+'
   * [] ==> 无变化.
   * [Seen] ==> 添加Seen标记
   * [Flagged] ==> 添加 Flagged标记
   * [Seen,Flagged] ==> 添加两个标记.
   *
   * '-'
   * [] ==> 无变化.
   * [Seen] ==> 如果有Seen则移除Seen标记
   * [Flagged] ==> 如果有Flagged,则移除Flagged
   * [Seen,Flagged] ==> 有任意一个标记都移除.
   *
   *
   *
   * @param attributes
   * @param modifyType
   */
  updateAttr(attributes: MailAttribute[], modifyType: '' | '+' | '-'):void {
    switch(modifyType){
      case '': {
        this.updateAttrBySet(attributes);
        break;
      }
      case '+':{
        this.updateAttrByAdd(attributes);
        break;
      }
      case '-':{
        this.updateAttrByClear(attributes)
        break;
      }
    }
  }

  /**
   * 获取当前的标志位信息.
   */
  getAttr():MailAttribute[] {
    let attr: MailAttribute[] = [];
    if(!this.isNew) {
      attr.push(MailAttribute.Seen);
    }
    if(this.isFlag) {
      attr.push(MailAttribute.Flagged)
    }
    return attr;
  }

  //首先移除所有标记,再根据入参添加
  updateAttrBySet(attributes: MailAttribute[]) : void {
    this.isNew = true;
    this.isFlag = false;
    // let envelop:MailEnvelope = this.getEnvelope();
    attributes.forEach((attr)=>{
      switch(attr){
        case MailAttribute.Seen:{
          this.isNew = false;
          break;
        }
        case MailAttribute.Flagged:{
          this.isFlag = true;
          break;
        }
      }
    })
  }
  //根据入参添加
  updateAttrByAdd(attributes: MailAttribute[]) : void {
    attributes.forEach((attr)=>{
      switch(attr){
        case MailAttribute.Seen:{
          this.isNew = false;
          break;
        }
        case MailAttribute.Flagged:{
          this.isFlag = true;
          break;
        }
      }
    })
  }
  //根据入参移除
  updateAttrByClear(attributes: MailAttribute[]) : void {
    attributes.forEach((attr)=>{
      switch(attr){
        case MailAttribute.Seen:{
          this.isNew = true;
          break;
        }
        case MailAttribute.Flagged:{
          this.isFlag = false;
          break;
        }
      }
    })
  }
  /**
   * 获取envelop对应的MailEnvelop数据.
   * 时间需要转换为date.
   * @returns
   */
  getEnvelope(): MailEnvelope | null {
    if (!this._envelop) {
      if (this.envelope.length > 0) {
        this._envelop = JSON.parse(this.envelope) as MailEnvelope;
        let date = this._envelop.date;
        if (typeof date === 'string') {
          this._envelop.date = new Date(date as string);
        }
      }
    }
    return this._envelop;
  }

  /**
   * 保存邮件结构
   * @param structure
   */
  public setMailStructure(structure:MailStructure) : void {
    this._structure = structure;
    this.structure = JSON.stringify(structure);
  }

  getMailStructure(): MailStructure | null {
    if(!this._structure) {
      if(this.structure.length > 0) {
        this._structure = JSON.parse(this.structure) as MailStructure;
      }
    }
    return this._structure;
  }

  getMailBasic(): MailBasic {
    let mailBasic = {
      envelope: this.getEnvelope(),
      structure: this.getMailStructure(),
      attributes: this.getAttr(),
      id: this.mid,
      size: this.size
    }
    return mailBasic;
  }

  /**
   * set mail basic
   * 设置MailBasic的时候需要存
   * receiver
   * sender
   * subject
   * attachment_names 用于数据库查询.
   *
   * receivedDate 用于排序
   *
   * @param basic
   */
  setMailBasic(basic: MailBasic): void {
    this.size = basic.size;
    this.mid = basic.id;
    this.updateAttrBySet(basic.attributes);
    this.setEnvelope(basic.envelope);
    this.setMailStructure(basic.structure);
    this.receivedDate = basic.envelope.date.getTime();
    this.receiver = this.addressesToString(basic.envelope.to)
    this.cc = this.addressesToString(basic.envelope.cc)
    this.bcc = this.addressesToString(basic.envelope.bcc)
    this.sender = this.addressToString(basic.envelope.from)
    this.subject = basic.envelope.subject;
    let attachmentNames = this.getAttachmentsString(basic.structure);
    this.attachmentNames = attachmentNames;
  }

  /**
   * 将structure的filename组装成字符串用于搜索.
   * @param struct
   * @returns
   */
  getAttachmentsString(struct: MailStructure): string {
    return getMimeParts(struct,
      (mime) => !!mime.disposition?.type.startsWith("attachment")
    )
    .map(st => st.disposition?.params.filename || st.contentType.params.name)
    .join("")
    /*
    let names = [];
    this.parseStructure(names,struct);
    return names.join("");
    * */
  }

  /*
  parseStructure(attachments:string[],struct:MailStructure) {
    const ms = struct as MailTextStructure;
    if (!!(ms.disposition?.type == "attachment")) {
      attachments.push(ms.disposition?.params.filename);
    }
    const st = struct as MailMultipartStructure;
    if (st.subParts) {
      for (const sub of st.subParts) {
        this.parseStructure(attachments,sub);
      }
    }
  }
  * */

  /**
   *
   */
  addressToString(emailAddress: EmailAddress): string {
    return `${emailAddress.name}${emailAddress.email}`
  }

  addressesToString(emailAddress: EmailAddress[]): string {
    return emailAddress.map((emailAdress:EmailAddress)=>{
      return this.addressToString(emailAdress)
    }).join("");
  }

  /**
   * 更新envelop相关的两个字段.
   * @param envelop
   */
  setEnvelope(envelop: MailEnvelope): void {
    this._envelop = envelop;
    this.envelope = JSON.stringify(envelop);
  }


  /**
   * 邮件结构中的附件 如何 和 邮件附件相关联?
   * 通过判断是否有structure数据来确定是否解析过.
   *
   * partId.
   */

  needParse(): boolean {
    // return (!this.html && this.text.length == 0) && this.emlPath.length > 0
    return !this.parsed && this.emlPath.length > 0
  }



  // /**
  //  * 获取附件.
  //  * @param partId
  //  * @returns
  //  */
  // getAttachement(partId:string):AttachmentModel {
  //   let attchs = this.getAttachments();
  //   let model = attchs.find((item)=>{
  //     return (item.partId == partId);
  //   })
  //   return model;
  // }
}
